#include <windows.h>
#include <d3d9.h>
#include <d3dx9tex.h>
#include <Dxerr.h>  
#include <stdio.h>

#include "grid.h"

HINSTANCE hInst;				// holds the instance for this app
HWND wndHandle;					// global window handle

LPDIRECT3D9             pD3D;
LPDIRECT3DDEVICE9       pd3dDevice;

// camera variables
D3DXMATRIX matView;					// the view matrix
D3DXMATRIX matProj;					// the projection matrix
D3DXVECTOR3 camPos;					// the position of the camera
D3DXVECTOR3 camLook;				// where the camera is pointing
D3DXVECTOR3 camDir;					// camera direction vector
float cameraAngle;
bool Wireframe = false;

Grid                    *pGrid;


LPDIRECT3DTEXTURE9 texture = NULL;

////////////////////////////////////////////// forward declarations
bool    initWindow(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

bool initDirect3D(HWND hwnd);
void shutdownDirect3D(void);

void createCamera(float nearClip, float farClip);
void moveCamera(D3DXVECTOR3 vec);
void pointCamera(D3DXVECTOR3 vec);

#define SCREEN_WIDTH  1024
#define SCREEN_HEIGHT 768

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	HRESULT hr;
	// call our function to init and create our window
	if (!initWindow(hInstance))
	{
		MessageBox(NULL, "Unable to create window", "ERROR", MB_OK);
		return false;
	}

	if (!initDirect3D(wndHandle))
	{
		MessageBox(NULL, "Unable to init Direct3D", "ERROR", MB_OK);
		return false;
	}


	camPos = D3DXVECTOR3(0.0f, 40.0f, -20.0f);
	camLook = D3DXVECTOR3(0.0f, 40.0f, 0.0f);
	cameraAngle = atan2f(camLook.z - camPos.z, camLook.x - camPos.x);
	
	D3DXVECTOR3 v0(-500.0f, 0.0f, -500.0f);
	D3DXVECTOR3 v1( 500.0f, 0.0f, -500.0f);
	D3DXVECTOR3 v2( 500.0f, 0.0f,  500.0f);
	D3DXVECTOR3 v3(-500.0f, 0.0f,  500.0f);

	pGrid = new Grid(101, 101, &v0, &v1, &v2, &v3);
	pGrid->CreateDirectXMesh(pd3dDevice);

	D3DXCreateTextureFromFile(pd3dDevice,"grass.jpg", &texture);

	// Main message loop:
	// Enter the message loop
	MSG msg; 
	ZeroMemory( &msg, sizeof(msg) );
	while( msg.message!=WM_QUIT )
	{
		// check for messages
		if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
		{
			TranslateMessage( &msg );
			DispatchMessage( &msg );
		}
		// this is called when no messages are pending
		else
		{
			createCamera(0.1f, 1000.0f);		// near clip plane, far clip plane
			
			pGrid->GetGridPoint(&camPos, camPos.x, camPos.z);
			camPos.y += 10.0f;
			
			moveCamera(camPos);
			pointCamera(camLook);


			// Clear the backbuffer to a black color
			pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 
							  D3DCOLOR_XRGB(5, 5, 5), 1.0f, 0 );

			pd3dDevice->BeginScene();

			pd3dDevice->SetTexture(0, texture);

			pGrid->DrawDirectXMesh(pd3dDevice);

			pd3dDevice->EndScene();

			// Present the backbuffer contents to the display
			pd3dDevice->Present( NULL, NULL, NULL, NULL );
		}
	}

	// release and shutdown Direct3D
	shutdownDirect3D();

	return (int) msg.wParam;
}

bool initWindow(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX);
	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= 0;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= "DirectX Lab 4 - Wk 4 Ronald Ram";
	wcex.hIconSm		= 0;
	RegisterClassEx(&wcex);

	wndHandle = CreateWindow("DirectX Lab 4 - Wk 4 Ronald Ram", 
							 "DirectX Lab 4 - Wk 4 Ronald Ram", 
							 WS_OVERLAPPEDWINDOW,
							 CW_USEDEFAULT, 
							 CW_USEDEFAULT, 
							 SCREEN_WIDTH, 
							 SCREEN_HEIGHT, 
							 NULL, 
							 NULL, 
							 hInstance, 
							 NULL);
   if (!wndHandle)
	  return false;
   
   ShowWindow(wndHandle, SW_SHOW);
   UpdateWindow(wndHandle);

   return true;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	switch (message) 
	{
		case WM_DESTROY:
			PostQuitMessage(0);
			break;
	case WM_KEYDOWN:
		int fwKeys;
		LPARAM keyData;
		fwKeys = (int)wParam;    // virtual-key code 
		keyData = lParam;          // key data 

		switch(fwKeys)
		{
		case VK_UP:
			{
				// update camera position by 4.0
				camDir = D3DXVECTOR3(cosf(cameraAngle) * 4.0f, 0.0f, sinf(cameraAngle) * 4.0f);
				camPos += camDir;
				// update camera look to be 20.0 ahead of the camera postition
				camDir = D3DXVECTOR3(cosf(cameraAngle) * 20.0f, 0.0f, sinf(cameraAngle) * 20.0f);
				camLook = camPos + camDir;
			}
			break;
		case VK_DOWN:
			{
				// update camera position by -4.0
				camDir = D3DXVECTOR3(cosf(cameraAngle) * 4.0f, 0.0f, sinf(cameraAngle) * 4.0f);
				camPos -= camDir;
				// update camera look to be 20.0 ahead of the camera position
				camDir = D3DXVECTOR3(cosf(cameraAngle) * 20.0f, 0.0f, sinf(cameraAngle) * 20.0f);
				camLook = camPos + camDir;
			}
			break;
		case VK_LEFT:
			{
				cameraAngle += 0.1f;
				// wrap the angle
				if (cameraAngle > (PI * 2)) { cameraAngle - (PI * 2); }
				if (cameraAngle < 0) { cameraAngle + (PI * 2); }

				camDir = D3DXVECTOR3(cosf(cameraAngle) * 20.0f, 0.0f, sinf(cameraAngle) * 20.0f);
				camLook = camPos + camDir;

			}

			break;
		case VK_RIGHT:
			{
				cameraAngle -= 0.1f;
				// wrap the angle
				if (cameraAngle > (PI * 2)) { cameraAngle - (PI * 2); }
				if (cameraAngle < 0) { cameraAngle + (PI * 2); }

				camDir = D3DXVECTOR3(cosf(cameraAngle) * 20.0f, 0.0f, sinf(cameraAngle) * 20.0f);
				camLook = camPos + camDir;
			}
			break;
		case 'W':
					   
			break;
		case VK_ESCAPE:
			PostQuitMessage(0);
			break;
		default:
			break;
		}

		break;

	default:
		break;
	}
	return DefWindowProc(hWnd, message, wParam, lParam);
}

bool initDirect3D(HWND hwnd)
{
	if( NULL == ( pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
		return false;
	
	D3DPRESENT_PARAMETERS d3dpp; 
	ZeroMemory( &d3dpp, sizeof(d3dpp) );
	d3dpp.Windowed = TRUE;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
	d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
	d3dpp.BackBufferCount  = 1;
	d3dpp.BackBufferHeight = SCREEN_HEIGHT;
	d3dpp.BackBufferWidth  = SCREEN_WIDTH;
	d3dpp.hDeviceWindow    = hwnd;
	d3dpp.EnableAutoDepthStencil = true;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	
	if( FAILED( pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hwnd,
									  D3DCREATE_SOFTWARE_VERTEXPROCESSING,
									  &d3dpp, &pd3dDevice ) ) )
	return false;

	if (Wireframe) {
		pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
	} else {
		pd3dDevice->SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
	}
	pd3dDevice->SetRenderState(D3DRS_LIGHTING, false);

	pd3dDevice->SetRenderState(D3DRS_ZENABLE, true);
	
	return true;
}

void shutdownDirect3D(void)
{
	if( pd3dDevice != NULL) 
	{
		pd3dDevice->Release();
		pd3dDevice = NULL;
	}
	if( pD3D != NULL)
	{
		pD3D->Release();
		pD3D = NULL;
	}
}

/*************************************************************************
* createCamera
* creates a virtual camera
*************************************************************************/
void createCamera(float nearClip, float farClip)
{
	//Here we specify the field of view, aspect ration and near and far clipping planes.
	D3DXMatrixPerspectiveFovLH(&matProj, D3DX_PI/4, float(640)/480, nearClip, farClip);
	pd3dDevice->SetTransform(D3DTS_PROJECTION, &matProj);

}

/*************************************************************************
* moveCamera
* moves the camera to a position specified by the vector passed as a 
* parameter
*************************************************************************/
void moveCamera(D3DXVECTOR3 vec)
{
	camPos = vec;
}

/*************************************************************************
* pointCamera
* points the camera a location specified by the passed vector
*************************************************************************/
void pointCamera(D3DXVECTOR3 vec)
{
	camLook = vec;

	D3DXMatrixLookAtLH(&matView, &camPos,		//Camera Position
								 &camLook,		//Look At Position
								 &D3DXVECTOR3(0.0f, 1.0f, 0.0f));		//Up Direction

	pd3dDevice->SetTransform(D3DTS_VIEW, &matView);
}